//============================================================================
// Copyright (C) 2003-2010 Brett R. Jones 
// All Rights Reserved
//
// You may redistribute and/or modify for non commercial and commercial uses 
// provided this copyright notice remains in place and is not modified
//
// This code is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// brettjones1900@gmail.com
// http://www.p2panarchy.com
//============================================================================

#include "VxUtilLib.h"
 #include <stdarg.h>


//---------------------------------------------------------------------------
//=== constructor ===//
DbBase::DbBase() :
	  m_bDbInitialized(0)
	, m_poDb(0)
{
	memset( m_aiSqlHandles, 0, sizeof( m_aiSqlHandles ) );
}
//---------------------------------------------------------------------------
//=== destructor ===//
DbBase::~DbBase()
{
}

//---------------------------------------------------------------------------
//! Initialize the database
RCODE DbBase::DbStartup( const char * pDbName )
{
	RCODE rc = 0;
    char tmpDir[ MAX_PATH ];
    char* pTemp;

	vx_assert( pDbName );
	m_strDbFileName = pDbName;
        
    // create paths and database if neccesary
    if( ! VxFileExists( m_strDbFileName.c_str() ) )
    {
        //create db 
        strcpy( tmpDir, m_strDbFileName.c_str() );
        pTemp = strrchr( tmpDir, '/' );
        if( NULL == pTemp )
        {
		    log_msg( 0, "DbBase: error creating database directory\n" );
		}
        pTemp[0] = '\0';
        VxMakeDirectory( tmpDir );
      
        rc = DbCreateDatabase();
        if( 0 != rc )
        {
            log_msg( 0,"DbBase:Cannot create database %s\n", m_strDbFileName.c_str() );
            return rc;
        }
    }
    m_bDbInitialized = TRUE;
    log_msg( 0, "DbBase: %s initialized\n", m_strDbFileName.c_str() );
    return 0;
}

//---------------------------------------------------------------------------
//! shutdown the database
RCODE DbBase::DbShutdown( void )
{
	return 0;
}
//---------------------------------------------------------------------------
//! archive database into given directory
RCODE DbBase::DbArchive( const char * pArchiveDirectory, BOOL bClearExistingDb )
{
	/*
	char ArchiveDir[MAX_PATH];
	char ArchiveDbPath[MAX_PATH];
	
	if (!g_bAcctDbInitialized)
	{
		if(0 != StartupAcctDb(DEFAULT_EXEC_PATH))
		{
			log_msg(SRV_DB_ACCT_ERR, "ArchiveDay:Storage Failed to Init\n");
			return -1;
		}
	}
	sprintf(ArchiveDir, "%s%s", g_atcAcctDbExecPath, DEFAULT_ACCT_ARCHIVE_DIR);
	if(0 != VxMakeDirectory(ArchiveDir))
	{
		log_msg(SRV_DB_ACCT_ERR,"ArchiveDay:Cannot create archive directory %s\n", ArchiveDir);
		return -1;
	}
	char TimeStamp[100];
	VxGetTimeStamp(TimeStamp, 100);
	sprintf(ArchiveDbPath, "%s%sacct_db%s", g_atcAcctDbExecPath, DEFAULT_ACCT_ARCHIVE_DIR, TimeStamp);
	if ( 0 == (VxCopyFile( g_atcAcctDbPath, ArchiveDbPath )))
	{
		VxDeleteFile( g_atcAcctDbPath );
	}
	else
	{
		log_msg(SRV_DB_ACCT_ERR, "AchiveAcctDb:Error copying db file\n");
		return -1;
	}
	if(0 != StartupAcctDb(g_atcAcctDbExecPath))
	{
		log_msg(SRV_DB_ACCT_ERR, "ArchiveAcctDb:Storage Failed to Init New Db\n");
		return -1;
	}
	*/
	return 0;	
}

//---------------------------------------------------------------------------
//! create initial database
RCODE DbBase::DbCreateDatabase( void )
{
	sqlite3 *db;
    int retval;
	RCODE rc;
    
    retval = sqlite3_open( m_strDbFileName.c_str(), &db);
    if (!(SQLITE_OK == retval))
    {
        log_msg( 0, "DbCreateDatabase:Unable to open databse %s\n", m_strDbFileName.c_str() );
        sqlite3_close(db);
        return -1;
    }
    // close database.. Create tables will reopen
    sqlite3_close(db);
    // make tables in database
    rc = DbCreateTables();
	if( rc )
    {
        log_msg( 0, "CreateDataBase:Unable to create AcctTable in db %s\n", m_strDbFileName.c_str() );
    }
    return rc;
}

//---------------------------------------------------------------------------
//! create initial tables in the database ( derived classes should override )
RCODE DbBase::DbCreateTables( void )
{

	return 0;
}
//---------------------------------------------------------------------------
//! open the database
RCODE DbBase::DbOpen( void )
{
    int retval;
	vx_assert( m_strDbFileName.size() );
    retval = sqlite3_open( m_strDbFileName.c_str(), &m_poDb );
    if (!(SQLITE_OK == retval))
    {
        log_msg( 0, "DbBase:Unable to open db %s\n", m_strDbFileName.c_str() );
        sqlite3_close(m_poDb);
		m_poDb = NULL;
        return -1;
    }
	vx_assert( m_poDb );
	return 0;
}
//---------------------------------------------------------------------------
//! close the database
RCODE DbBase::DbClose( void )
{
	if( m_poDb )
	{
        sqlite3_close(m_poDb);
		m_poDb = NULL;
		return 0;
	}
    log_msg( 0, "DbBase:Tried to close allready closed db %s\n", m_strDbFileName.c_str() );
	return -1;
}

//---------------------------------------------------------------------------
//! execute Sql statement
RCODE DbBase::SqlExec( const char * pSqlString, ... )
{
    char SQL_Statement[65535];

	va_list arg_ptr;
	va_start(arg_ptr, pSqlString);
	#ifdef ANDROID_PLATFORM
		vsprintf(SQL_Statement, pSqlString, arg_ptr);
	#else
		vsprintf(SQL_Statement, pSqlString, (char *)arg_ptr);
	#endif
	va_end(arg_ptr);
	return SqlExec( SQL_Statement );
}

//---------------------------------------------------------------------------
//! execute Sql statement
RCODE DbBase::SqlExec( char * SQL_Statement )
{
    char *SQL_Error;
    int retval;
	RCODE rc;

	rc = DbOpen();
	if( 0 == rc )
	{
		retval = sqlite3_exec( m_poDb, SQL_Statement, NULL, NULL, &SQL_Error );
		if (!(SQLITE_OK == retval))
		{
			log_msg( 0, "DbBase:sqlite3_exec:%s", SQL_Error );
			sqlite3_free(SQL_Error);
			DbClose();
			return -1;
		}
	}
	rc = DbClose();
    return rc;
}
//---------------------------------------------------------------------------
//! execute Sql statement without opening or closing the db
RCODE DbBase::RunQuery( const char * pQueryString ) 
{
	sqlite3_stmt * poSqlStatement;
	const char * pTail;
	int iResult = sqlite3_prepare_v2( m_poDb, pQueryString, strlen( pQueryString ), &poSqlStatement, &pTail );
	if( iResult != SQLITE_OK ) 
	{
		log_msg( 0, "DbBase::RunQuery:  %s", sqlite3_errmsg( m_poDb ));
		return iResult;
	}

	iResult = sqlite3_step(poSqlStatement);
	if( ( iResult != SQLITE_DONE ) && ( iResult != SQLITE_ROW )) 
	{
		log_msg( 0, "DbBase::RunQuery:  %s", sqlite3_errmsg( m_poDb ));
		return iResult;
	}

	iResult = sqlite3_finalize( poSqlStatement );
	if( iResult != SQLITE_OK ) 
	{
		log_msg( 0, "DbBase::RunQuery: %s", sqlite3_errmsg( m_poDb ));
		return iResult;
	}
	return 0;
}

//---------------------------------------------------------------------------
//! execute sql then return int from first column and close query
S32 DbBase::QueryAndReturnFirstColumnInteger( const char * pSqlStatment )
{
	StartDataQuery( pSqlStatment );
	GetDbRow();
	S32 s32Value = GetDbS32(0);
	CloseQuery();

	return s32Value;
}

//---------------------------------------------------------------------------
//! Runs a query for multiple rows of data
RCODE DbBase::StartDataQuery( const char * pSqlString, int iSqlHandleIdx )
{
	vx_assert( pSqlString );
	sqlite3_stmt * poSqlStatement = NULL;
	const char * pTail;
	int iResult = sqlite3_prepare_v2( m_poDb, pSqlString, strlen( pSqlString ), &poSqlStatement, &pTail );
	if( SQLITE_OK != iResult ) 
	{
		log_msg( 0, "DatabaseClass::StartDataQuery: error %s\n", sqlite3_errmsg(m_poDb) );
		return iResult;
	}
	m_aiSqlHandles[ iSqlHandleIdx ] = poSqlStatement;

	return 0;
}

//---------------------------------------------------------------------------
//! fetch next row from table
RCODE DbBase::GetDbRow( int iSqlHandleIdx )
{
	vx_assert( iSqlHandleIdx < MAX_CONCURRENT_SQL_QUERIES );
	int iResult = sqlite3_step( m_aiSqlHandles[ iSqlHandleIdx ] );
	switch( iResult ) 
	{
	case SQLITE_ROW:
		return 0;
		break;
	case SQLITE_DONE:
		return 1;
		break;
	default:
		break;
	}
	return -1;
}

//---------------------------------------------------------------------------
//! close query
RCODE DbBase::CloseQuery( int iSqlHandleIdx )
{
	int iResult = sqlite3_finalize( m_aiSqlHandles[ iSqlHandleIdx ] );
	if( SQLITE_OK != iResult ) 
	{
		log_msg( 0, "DatabaseClass::CloseQuery: error %s\n", sqlite3_errmsg(m_poDb) );
		return iResult;
	}
	return 0;
}

//---------------------------------------------------------------------------
//! return 32 bit integer from column
S32 DbBase::GetDbS32( int iColumnIdx, int iSqlHandle )
{
	return sqlite3_column_int( m_aiSqlHandles[ iSqlHandle ], iColumnIdx );
}

//---------------------------------------------------------------------------
//! return 64 bit integer from column
S64 DbBase::GetDbS64( int iColumnIdx, int iSqlHandle )
{
	return sqlite3_column_int64( m_aiSqlHandles[ iColumnIdx ], iColumnIdx );
}

//---------------------------------------------------------------------------
//! return float from column
F32 DbBase::GetDbF32( int iColumnIdx, int iSqlHandle )
{
	return (float)sqlite3_column_double( m_aiSqlHandles[ iColumnIdx ], iColumnIdx );
}
//---------------------------------------------------------------------------
//! return float from column
F64 DbBase::GetDbF64( int iColumnIdx, int iSqlHandle )
{
	return sqlite3_column_double( m_aiSqlHandles[ iColumnIdx ], iColumnIdx );
}

//---------------------------------------------------------------------------
//! return string from column
const char * DbBase::GetDbString( int iColumnIdx, int iSqlHandle )
{
	return (const char *)sqlite3_column_text( m_aiSqlHandles[ iColumnIdx ], iColumnIdx );
}

//---------------------------------------------------------------------------
//! get the row id of the last inserted row
S64 DbBase::GetLastInsertId( void )
{
	if( m_poDb )
	{
		return sqlite3_last_insert_rowid( m_poDb );
	}
	return 0;
}




